// 版权所有2021 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package types2

import (
	"cmd/compile/internal/syntax"
	"fmt"
)

// 实例化使用给定的类型参数实例化类型类型。
// 类型必须是*命名或*签名类型，必须是泛型，并且
// 其类型参数的数量必须与提供的类型参数的数量匹配
// 论据。结果是一个新的实例化（非泛型）类型的
// 相同种类（或*命名或*签名）。类型参数
// 未根据类型参数的约束进行检查。
// 任何附加到*命名的方法都只是简单地复制；它们不是
// 实例化。
func Instantiate(pos syntax.Pos, typ Type, targs []Type) (res Type) {
	// TODO（gri）此代码基本上与prolog相同
	// 在Checker.instance中。因素
	var tparams []*TypeName
	switch t := typ.(type) {
	case *Named:
		tparams = t.tparams
	case *Signature:
		tparams = t.tparams
		defer func() {
			// 如果我们在某个地方发生了意外的失败，不要惊慌失措
			// 断言物（*签名）。检查*签名，以防类型[无效]
			// 他回来了。
			if _, ok := res.(*Signature); !ok {
				return
			}
			// 如果签名不使用其类型参数，则subst
			// 不会复制。在这种情况下，现在就复制一份（所以
			// 我们可以将tparams设置为零（不产生副作用）。
			if t == res {
				copy := *t
				res = &copy
			}
			// 实例化泛型签名后，它不是泛型的
			// 不再我们需要将tparams设置为零。
			res.(*Signature).tparams = nil
		}()

	default:
		panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
	}

	// 提供的类型数必须与类型参数数匹配
	if len(targs) != len(tparams) {
		panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams)))
	}

	if len(tparams) == 0 {
		return typ // 无事可做（小优化）
	}

	smap := makeSubstMap(tparams, targs)
	return (*Checker)(nil).subst(pos, typ, smap)
}
